package com.yc.exam.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yc.exam.entity.*;
import com.yc.exam.mapper.ExamMapper;
import com.yc.exam.mapper.TopicMapper;
import com.yc.exam.service.*;
import com.yc.exam.vo.AnswerSheetVo;
import com.yc.exam.vo.ExamVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author 王妍
 * @since 2022-03-23
 */
@Service
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IExamService {

  @Autowired private IExamTopicService examTopicService;

  @Autowired private ExamMapper examMapper;

  @Autowired private TopicMapper topicMapper;

  @Autowired private IExaminatioStatusService examinatioStatusService;

  @Autowired private IStudentGradeService studentGradeService;

  @Autowired private IAnswerResultsService resultsService;

  @Autowired private IStudentExamService studentExamService;

  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean saveOrUpdate(ExamVo exam) {
    boolean result;
    final Exam examEntity = new Exam();
    BeanUtils.copyProperties(exam, examEntity);
    result = saveOrUpdate(examEntity);
    result =
        examTopicService.remove(
                new LambdaQueryWrapper<ExamTopic>().eq(ExamTopic::getExamId, examEntity.getId()))
            && result;
    final List<ExamTopic> topicList =
        Arrays.stream(exam.getTopicIds().split(","))
            .map(Long::parseLong)
            .map(
                topicId -> {
                  final ExamTopic examTopic = new ExamTopic();
                  examTopic.setExamId(examEntity.getId());
                  examTopic.setTopicId(topicId);
                  return examTopic;
                })
            .collect(Collectors.toList());
    result = examTopicService.saveBatch(topicList) && result;
    return result;
  }

  @Override
  public Page<ExamVo> pageVo(Page<ExamVo> examPage, ExamVo exam) {
    return examMapper.selectPageVo(examPage, exam);
  }

  @Override
  public Page<ExamVo> getPageByStudentName(
      Page<ExamVo> examPage, ExamVo exam, String studentUserName) {
    return examMapper.selectPageVoByStudentName(examPage, exam, studentUserName);
  }

  @Override
  @Transactional
  public Long auto(ExamVo examVo) {
    final Exam exam = new Exam();
    BeanUtils.copyProperties(examVo, exam);
    save(exam);
    int count = examVo.getCount1();
    obtain(count, examVo, exam, 1);
    int count2 = examVo.getCount2();
    obtain(count2, examVo, exam, 2);
    return exam.getId();
  }

  @Override
  @Transactional
  public Long finishUpExam(Long id, AnswerSheetVo answerSheetVo) {
    final Long examId = answerSheetVo.getExamId();
    // 1. 修改学生的考试状态
    final ExaminatioStatus entity = new ExaminatioStatus();
    entity.setExamId(examId);
    entity.setStatus(true);
    entity.setStudentId(id);
    examinatioStatusService.saveOrUpdate(entity);
    // 2. 录入学生的考试成绩
    final List<AnswerSheetVo.Info> infoList = answerSheetVo.getResult();
    final List<Long> collect =
        infoList.stream().map(AnswerSheetVo.Info::getTopicId).collect(Collectors.toList());
    final HashMap<Long, Topic> topicHashMap = new HashMap<>();
    topicMapper.selectBatchIds(collect).forEach(topic -> topicHashMap.put(topic.getId(), topic));
    final long sum = infoList.stream().mapToLong(getInfoToLongFunction(topicHashMap)).sum();
    // 保存学生成绩
    final StudentGrade studentGrade = new StudentGrade();
    studentGrade.setStudentId(id);
    studentGrade.setGrade((int) sum);
    studentGrade.setExam(examId);
    studentGradeService.save(studentGrade);
    // 保存学生答题记录
    infoList.forEach(
        info -> {
          final AnswerResults answerResults = new AnswerResults();
          answerResults.setExamId(examId);
          answerResults.setTopicId(info.getTopicId());
          answerResults.setOption(info.getOption());
          resultsService.save(answerResults);
        });

    return sum;
  }

  @Override
  public void send(Long examId, Long[] ids) {
    List<StudentExam> studentExams = Arrays.stream(ids).map(id -> {
      StudentExam exam = new StudentExam();
      exam.setExamId(examId);
      exam.setStudentId(id);
      return exam;
    }).collect(Collectors.toList());
    studentExamService.saveBatch(studentExams);
  }

  private ToLongFunction<AnswerSheetVo.Info> getInfoToLongFunction(
      HashMap<Long, Topic> topicHashMap) {
    return info -> {
      final Topic topic = topicHashMap.get(info.getTopicId());
      if (topic == null
          || topic.getType() == 1
          || !Objects.equals(topic.getRightAnswer(), info.getOption())) {
        return 0;
      }
      return topic.getFraction();
    };
  }

  private void obtain(int count, ExamVo examVo, Exam exam, Integer type) {

    int number1 = (int) (count * 0.6);
    int number2 = (int) (count * 0.2);
    int number3 = count - number1 - number2;
    int difficulty = 3;
    // 查询简单题目
    List<Topic> topics1 =
        topicMapper.selectTopicByDifficultyAndCount(
            exam.getCourseId(), type, examVo.getDifficulty(), number1);
    difficulty -= examVo.getDifficulty();
    List<Topic> topics2 =
        topicMapper.selectTopicByDifficultyAndCount(exam.getCourseId(), type, difficulty, number2);
    difficulty = 3 - examVo.getDifficulty() - difficulty;
    List<Topic> topics3 =
        topicMapper.selectTopicByDifficultyAndCount(exam.getCourseId(), type, difficulty, number3);
    topics1.addAll(topics2);
    topics1.addAll(topics3);
    final List<ExamTopic> collect =
        topics1.stream()
            .map(
                topic -> {
                  final ExamTopic examTopic = new ExamTopic();
                  examTopic.setExamId(exam.getId());
                  examTopic.setTopicId(topic.getId());
                  return examTopic;
                })
            .collect(Collectors.toList());
    examTopicService.saveBatch(collect);
  }
}
